package sceneObjects;

import utils.*;
import main.Intersection;
import main.Parseable;
import main.Parser;

public class RectPlane extends abstractSceneObject implements sceneObject, Parseable {
    private Point3D p0, p1, p2;
    private Vector normal, v0, v1;

    @Override
    public boolean setParameter(String name, String[] args) {
	if (super.setParameter(name, args)) {
	    return true;
	} else if (name.equals("p0")) {
	    if (args.length != 3) {
		System.out.println("bad parameters");
		return false;
	    }
	    p0 = new Point3D(Parser.stringArrToDoubleArr(args));
	    return true;
	} else if (name.equals("p1")) {
	    if (args.length != 3) {
		System.out.println("bad parameters");
		return false;
	    }
	    p1 = new Point3D(Parser.stringArrToDoubleArr(args));
	    return true;
	} else if (name.equals("p2")) {
	    if (args.length != 3) {
		System.out.println("bad parameters");
		return false;
	    }
	    p2 = new Point3D(Parser.stringArrToDoubleArr(args));
	    return true;
	}

	// We didn't find any parameter
	return false;
    }

    public RectPlane(Point3D p0, Point3D p1, Point3D p2) {
	this.p0 = new Point3D(p0);
	this.p1 = new Point3D(p1);
	this.p2 = new Point3D(p2);

	finalizeObject();
    }

    public RectPlane() {

    }

    private Vector calcNormal() {
	Vector v1 = new Vector(p0, p1);
	Vector v2 = new Vector(p0, p2);

	return v1.crossProduct(v2).normalize();
    }

    @Override
    public boolean finalizeObject() {
	if (!super.finalizeObject()) {
	    return false;
	}
	// Checks if the 3 points exists
	if (p0 == null || p1 == null || p2 == null) {
	    System.out.println("Error: Missing 3 points");
	    return false;
	}

	// Check that the 3 points aren't colinear
	if (GeoUtils.isCollinear(p0, p1, p2)) {
	    System.out.println("Error: Rectangle points are colinear");
	    return false;
	}

	normal = calcNormal();

	// Compute parameterization vectors
	v0 = new Vector(p0, p2);
	v1 = new Vector(p0, p1);

	return true;
    }

    public Point3D getParameterization(Point3D p) {
	Vector v2 = new Vector(p0, p);

	// Compute dot products
	double dot00 = v0.dotProduct(v0);
	double dot01 = v0.dotProduct(v1);
	double dot02 = v0.dotProduct(v2);
	double dot11 = v1.dotProduct(v1);
	double dot12 = v1.dotProduct(v2);

	// Compute barycentric coordinates
	double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
	double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
	double v = (dot00 * dot12 - dot01 * dot02) * invDenom;

	return new Point3D(u, v, 0);
    }

    public Point3D getParameterization(Intersection intersect) {
	return getParameterization(intersect.getPoint());
    }

    public boolean isCamera() {
	return false;
    }

    public boolean isLight() {
	return false;
    }

    @Override
    public Intersection getIntersect(Ray ray) {
	Point3D pPlane = ray.getPlaneIntersection(new Plane(p0, normal));

	if (pPlane == null) {
	    return null;
	}
	pPlane = pPlane.addVector(ray.getDirection().scale(-0.0001F));

	double u = getParameterization(pPlane).getX();
	double v = getParameterization(pPlane).getY();

	if (u > 0 && u < 1 && v > 0 && v < 1) {
	    return new Intersection(pPlane, this);
	} else {
	    return null;
	}

    }

    public Vector getNormalAtPoint(Intersection intersect) {
	return new Vector(normal);
    }
}
